package maple.thelittleredbook.recommendation.service;


import lombok.extern.slf4j.Slf4j;
import maple.thelittleredbook.recommendation.entity.ContentFeature;
import maple.thelittleredbook.recommendation.mapper.ContentFeatureMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;


import java.util.List;
import java.util.concurrent.Semaphore;

/**
 * 内容特征服务
 * 负责内容特征生成的调度和管理
 */
@Slf4j
@Service
public class ContentFeatureService {

    private final ContentFeatureMapper contentFeatureMapper;
    private final AsyncContentFeatureService asyncContentFeatureService;

    // 批量处理大小，可通过配置文件调整
    @Value("${content-feature.batch-size}")
    private int batchSize;

    // 最大并发数，可通过配置文件调整
    @Value("${content-feature.max-concurrent}")
    private int maxConcurrent;

    // 等待重试间隔（毫秒）
    @Value("${content-feature.wait-retry-interval}")
    private long waitRetryInterval;

    // 连续无法锁定的最大尝试次数
    @Value("${content-feature.max-no-lock-attempts}")
    private int maxNoLockAttempts;

    @Autowired
    public ContentFeatureService(ContentFeatureMapper contentFeatureMapper,
                                 AsyncContentFeatureService asyncContentFeatureService) {
        this.contentFeatureMapper = contentFeatureMapper;
        this.asyncContentFeatureService = asyncContentFeatureService;
    }

    /**
     * 处理内容特征生成任务
     * 由定时任务调用的主要方法
     */
    public void processFeatureGeneration() {
        log.debug("开始处理内容特征生成任务");

        try {
            // 1. 获取所有待处理任务
            List<ContentFeature> pendingTasks = contentFeatureMapper.selectPendingTasks();

            if (pendingTasks.isEmpty()) {
                log.debug("没有待处理的内容特征任务");
                return;
            }

            log.info("找到{}个待处理的内容特征任务", pendingTasks.size());

            // 2. 创建信号量控制并发数量
            Semaphore semaphore = new Semaphore(maxConcurrent);

            // 3. 逐个尝试锁定任务，直到处理够batchSize个或遍历完所有任务
            int processedCount = 0;
            int noLockAttempts = 0; // 连续无法锁定的尝试次数

            for (ContentFeature task : pendingTasks) {
                // 检查是否已处理够数量
                if (processedCount >= batchSize) {
                    log.debug("已处理{}个任务，达到批量大小限制", processedCount);
                    break;
                }

                // 检查是否连续太多次无法锁定
                if (noLockAttempts >= maxNoLockAttempts) {
                    log.info("连续{}次无法锁定任务，其他进程可能已处理完所有可用任务，提前结束", noLockAttempts);
                    break;
                }

                // 等待直到有可用的信号量
                try {
                    boolean acquired = semaphore.tryAcquire();
                    if (!acquired) {
                        log.debug("达到最大并发数{}，等待{}ms后重试", maxConcurrent, waitRetryInterval);
                        Thread.sleep(waitRetryInterval);

                        // 再次尝试获取信号量
                        acquired = semaphore.tryAcquire();
                        if (!acquired) {
                            log.debug("等待后仍无法获取信号量，跳过当前任务");
                            continue; // 跳过当前任务，继续尝试下一个
                        }
                    }

                    // 尝试原子性锁定任务
                    boolean locked = tryLockSingleTask(task.getId());
                    if (locked) {
                        // 锁定成功，启动异步处理
                        asyncContentFeatureService.processSingleTaskAsync(task.getId(), semaphore);
                        processedCount++;
                        noLockAttempts = 0; // 重置连续失败计数
                        log.debug("成功锁定并启动异步任务，ID: {}", task.getId());
                    } else {
                        // 锁定失败（可能被其他实例抢占），释放信号量
                        semaphore.release();
                        noLockAttempts++;
                        log.debug("任务已被其他实例锁定，跳过，ID: {}，连续失败次数: {}", task.getId(), noLockAttempts);
                    }

                } catch (InterruptedException e) {
                    log.warn("等待信号量时被中断", e);
                    Thread.currentThread().interrupt();
                    break;
                }
            }

            log.info("内容特征生成任务处理完成，成功锁定并启动{}个异步任务", processedCount);

        } catch (Exception e) {
            log.error("处理内容特征生成任务时发生异常", e);
        }
    }

    /**
     * 尝试锁定单个任务
     *
     * @param contentFeatureId 内容特征ID
     * @return 是否锁定成功
     */
    private boolean tryLockSingleTask(Long contentFeatureId) {
        try {
            // 使用专门的锁定方法
            int updateCount = contentFeatureMapper.lockSingleTask(contentFeatureId);
            return updateCount == 1;

        } catch (Exception e) {
            log.error("锁定任务时发生异常，ID: {}", contentFeatureId, e);
            return false;
        }
    }


    /**
     * 重置内容特征状态为待处理
     * 当内容的文本发生变化时调用
     *
     * @param contentId 内容ID
     */
    public void resetContentFeatureToPending(Long contentId) {
        try {
            int updateCount = contentFeatureMapper.resetContentFeatureToPending(contentId);
            log.info("重置内容特征状态为待处理，contentId: {}, 影响行数: {}", contentId, updateCount);
        } catch (Exception e) {
            log.error("重置内容特征状态失败，contentId: {}", contentId, e);
            // 这里不抛异常，避免影响主要的更新内容操作
        }
    }



    // ==================== 管理员API相关方法 ====================

    /**
     * 获取失败任务数量
     *
     * @return 失败任务数量
     */
    public Long getFailedTaskCount() {
        try {
            Long count = contentFeatureMapper.getFailedTaskCount();
            log.debug("查询失败任务数量: {}", count);
            return count;
        } catch (Exception e) {
            log.error("查询失败任务数量时发生异常", e);
            return 0L;
        }
    }

    /**
     * 重置所有失败任务为待处理状态
     *
     * @return 重置的任务数量
     */
    public Integer retryAllFailedTasks() {
        try {
            int resetCount = contentFeatureMapper.resetFailedTasks();
            log.info("重置失败任务为待处理状态，数量: {}", resetCount);
            return resetCount;
        } catch (Exception e) {
            log.error("重置失败任务时发生异常", e);
            throw new RuntimeException("重置失败任务失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取任务状态统计信息
     *
     * @return 包含各状态任务数量的Map
     */
    public java.util.Map<String, Long> getTaskStatusStatistics() {
        try {
            java.util.Map<String, Long> statistics = new java.util.HashMap<>();

            // 使用Mapper中的专门方法查询各状态数量
            statistics.put("PENDING", contentFeatureMapper.selectCount(
                    new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<ContentFeature>()
                            .eq("task_status", "PENDING")
            ));

            statistics.put("PROCESSING", contentFeatureMapper.selectCount(
                    new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<ContentFeature>()
                            .eq("task_status", "PROCESSING")
            ));

            statistics.put("COMPLETED", contentFeatureMapper.selectCount(
                    new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<ContentFeature>()
                            .eq("task_status", "COMPLETED")
            ));

            statistics.put("FAILED", contentFeatureMapper.getFailedTaskCount());

            log.debug("任务状态统计: {}", statistics);
            return statistics;

        } catch (Exception e) {
            log.error("获取任务状态统计时发生异常", e);
            return new java.util.HashMap<>();
        }
    }

    /**
     * 清理长时间处于处理中状态的任务
     * 可以在应用启动时或定期调用
     *
     * @param timeoutMinutes 超时分钟数，默认30分钟
     * @return 清理的任务数量
     */
    public Integer cleanupTimeoutProcessingTasks(Integer timeoutMinutes) {
        if (timeoutMinutes == null || timeoutMinutes <= 0) {
            timeoutMinutes = 30; // 默认30分钟
        }

        try {
            int cleanupCount = contentFeatureMapper.resetProcessingTasks();
            log.info("清理超时处理任务，数量: {}, 超时时间: {}分钟", cleanupCount, timeoutMinutes);
            return cleanupCount;
        } catch (Exception e) {
            log.error("清理超时处理任务时发生异常", e);
            throw new RuntimeException("清理超时任务失败: " + e.getMessage(), e);
        }
    }

    /**
     * 为新创建的内容创建特征记录
     * 在内容创建时调用
     *
     * @param contentId 内容ID
     * @return 创建的特征记录ID
     */
    public Long createContentFeature(Long contentId) {
        try {
            ContentFeature contentFeature = new ContentFeature();
            contentFeature.setContentId(contentId);
            contentFeature.setTaskStatus("PENDING");
            contentFeature.setRetryCount(0);

            contentFeatureMapper.insert(contentFeature);

            Long featureId = contentFeature.getId();
            log.info("为内容创建特征记录，contentId: {}, featureId: {}", contentId, featureId);
            return featureId;

        } catch (Exception e) {
            log.error("创建内容特征记录时发生异常，contentId: {}", contentId, e);
            throw new RuntimeException("创建内容特征记录失败: " + e.getMessage(), e);
        }
    }
}